package com.tong.embeddedvr.service.impl;

import com.jcraft.jsch.*;
import com.tong.embeddedvr.dto.HostAccountDTO;
import com.tong.embeddedvr.dto.HostDTO;
import com.tong.embeddedvr.service.SSHService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;

import java.io.*;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * TODO
 *
 * @author gcl
 * @version 1.0
 * @Description
 * @date 2021/3/6 14:21
 */
@Service
public class SSHServiceImpl implements SSHService {

    int state = 1;  //0表示connect线程尚未完成,

    private static Logger logger = LoggerFactory.getLogger(SSHServiceImpl.class);
    private ConcurrentHashMap<String,Object> channelMap = new ConcurrentHashMap<String,Object>();
    private static ExecutorService executorService = Executors.newCachedThreadPool();

    @Override
    public void useradd(HostDTO hostDTO,HostAccountDTO hostAccountDTO) throws Exception {
        logger.info("正在创建主机账号,用户名:[{}],密码:[{}]",hostDTO.getHostUsername(),hostDTO.getHostPassword());
        String key = UUID.randomUUID().toString();  //用于绑定进程的channel
        int a;
        executorService.execute(new Thread(){
            @Override
            public void run() {
                try {
                    initConnection(key,hostDTO);
                } catch (Exception e) {
                    logger.error("连接初始化失败");
                    logger.error("错误信息:[{}]",e.getMessage());
                }
            }
        });
        Channel channel = null;
        int tempNum=0;  //计数器
        while(++tempNum<=30&&(channel=(Channel)channelMap.get(key))==null){
            Thread.sleep(1000);
        }
        if(channel==null){
            throw new Exception("连接初始化失败");
        }
        String command = "useradd -s /bin/bash -g student -m " + hostAccountDTO.getHostAccountUsername();
        executeCommand(channel,command + "\n");
        Thread.currentThread().sleep(200);
        command = "passwd " + hostAccountDTO.getHostAccountUsername();
        executeCommand(channel,command + "\n");
        Thread.currentThread().sleep(200);
        command = hostAccountDTO.getHostAccountPassword();
        executeCommand(channel,command + "\n");  //密码修改时做特殊处理,后面加"\r"
        Thread.currentThread().sleep(200);
        executeCommand(channel,command + "\n");
        //指令无法正确触发可能是因为需要增加通道关闭的延时
        Thread.currentThread().sleep(200);
        channel.disconnect();
        channelMap.remove(key);
    }

    @Override
    public void userdel(HostDTO hostDTO,HostAccountDTO hostAccountDTO) throws Exception {
        System.out.println("正在删除账号");
        String key = UUID.randomUUID().toString();
        executorService.execute(new Thread(){
            @Override
            public void run() {
                try {
                    initConnection(key,hostDTO);
                } catch (Exception e) {
                    logger.error("连接初始化失败");
                    logger.error("错误信息:[{}]",e.getMessage());
                }
            }
        });
        Channel channel = null;
        int tempNum=0;  //计数器
        while(++tempNum<=30&&(channel=(Channel)channelMap.get(key))==null){
            Thread.sleep(200);
        }
        if(channel==null){
            throw new Exception("连接初始化失败");
        }
        String command = "userdel -r -f " + hostAccountDTO.getHostAccountUsername();
        executeCommand(channel,command + "\n");
        Thread.currentThread().sleep(200);  //这个延迟很重要
        channel.disconnect();
        channelMap.remove(key);
    }

    public void initConnection(String key,HostDTO hostDTO) throws JSchException, IOException {
        JSch jSch = new JSch();
        Session session = jSch.getSession(hostDTO.getHostUsername(), hostDTO.getHostIp(), 22);
        session.setConfig("StrictHostKeyChecking", "no");  //是否启用密匙,no会降低安全性
        session.setPassword(hostDTO.getHostPassword());
        //连接超时时间为30s
        session.connect(30000);

        //开启shell通道
        Channel channel = session.openChannel("shell");

        //通道连接,超时时间3s
        channel.connect(30000);

        //设置channel
        channelMap.put(key,channel);  //不要在阻塞之后的地方设置channel
        System.out.println("channel建立完成");

        //发送指令
        executeCommand(channel,"\r\n");

        //获取输出流
        InputStream inputStream = channel.getInputStream();
        try {
            //循环读取
            byte[] buffer = new byte[1024];  //缓冲区
            int i = 0;
            //如果没有数据来,线程会一直阻塞在这个地方数据,为了让主线程不受影响,需要多开一个线程
            while((i = inputStream.read(buffer))!=-1){
                System.out.println("返回信息:");
                System.out.println(new String(buffer,0,i));
                //注意:这里不能使用new TextMessage(Arrays.copyOfRange(buffer,0,i)).toString(),因为TextMessage是用于通过webSocket向前端回写
                logger.info(new String(buffer,0,i));
            }
        } catch (IOException e) {
            logger.error("输出流获取失败");
            logger.error("错误信息:[{}]",e.getMessage());
        }finally {
            session.disconnect();
            channel.disconnect();
            if(inputStream != null){
                inputStream.close();
            }
        }
    }

    private void executeCommand(Channel channel, String command) throws IOException {
        System.out.println("Command:" + command);
        command = command + "";
        if (channel != null) {
            OutputStream outputStream = channel.getOutputStream();
            outputStream.write(command.getBytes());
            outputStream.flush();
        }
    }
}
